﻿using Microsoft.EntityFrameworkCore.Metadata.Builders;
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;

using Kernelx.Common.DataAnnotations.Schema;
using Kernelx.Contexts;
using Kernelx.Contexts.Defaults;


namespace Microsoft.EntityFrameworkCore
{
    public static class EntityFrameworkCoreExtensions
    {
        private static readonly MethodInfo UseIdentityColumn;
        static EntityFrameworkCoreExtensions()
        {
            try
            {
                //仅开发模式下去尝试
                if (!WorkloadStartupContext.HasCurrent() || (WorkloadStartupContext.Current.GetWorkloadEnvironment()?.IsDevelopment ?? false))
                {
                    //尝试加载SqlServer的UseIdentityColumn扩展方法
                    var ass = Assembly.Load(new AssemblyName("Microsoft.EntityFrameworkCore.SqlServer"));
                    UseIdentityColumn = ass.GetType("Microsoft.EntityFrameworkCore.SqlServerPropertyBuilderExtensions")
                    ?.GetMethod("UseIdentityColumn", new[] { typeof(PropertyBuilder), typeof(int), typeof(int) });
                }
            }
            catch { }
        }

        /// <summary>
        /// 尝试反射查找UseIdentityColumn方法,找到后调用
        /// </summary>
        /// <param name="propertyBuilder"></param>
        /// <param name="seed"></param>
        /// <param name="increment"></param>
        private static void TryUseIdentityColumn(PropertyBuilder propertyBuilder, int seed = 1, int increment = 1)
        {
            UseIdentityColumn?.Invoke(null, new object[] { propertyBuilder, seed, increment });
        }

        /// <summary>
        /// 构建模型映射到存储库中的信息,(增加字段描述信息生成)
        /// </summary>
        /// <param name="modelBuilder"></param>
        /// <param name="entityType"></param>
        public static void BuilderModelMapping(this ModelBuilder modelBuilder, Type entityType)
        {
            //
            var eb = modelBuilder.Entity(entityType);
            var typeAtts = entityType.GetCustomAttributes(false);

            var eName = (typeAtts.FirstOrDefault(x => x is TableAttribute) as TableAttribute)?.Name ?? entityType.Name;



            if (typeAtts.FirstOrDefault(x => x is DisplayAttribute) is DisplayAttribute etyDisplay)
                eb.HasComment(etyDisplay.Description);
            if (typeAtts.FirstOrDefault(x => x is ViewMappedAttribute) is ViewMappedAttribute etyView)
                eb.ToView(eName);

            //仅开发模式下去尝试反射查找entityType的SeedData方法并调用 
            if (!WorkloadStartupContext.HasCurrent() || (WorkloadStartupContext.Current.GetWorkloadEnvironment()?.IsDevelopment ?? false))
            {
                var dataLoad = entityType.GetMethod("SeedData", BindingFlags.Static | BindingFlags.NonPublic);
                if (dataLoad != null && dataLoad.Invoke(null, null) is object[] data)
                {
                    eb.HasData(data);
                }
            }
            var props = entityType.GetProperties();
            foreach (var p in props)
            {
                //导航属性跳过
                if (p.PropertyType.IsClass && p.PropertyType != typeof(string))
                    continue;

                var attrs = p.GetCustomAttributes(false);
                if (attrs.FirstOrDefault(x => x is DatabaseGeneratedAttribute) is DatabaseGeneratedAttribute ga
                    && ga.DatabaseGeneratedOption == DatabaseGeneratedOption.Identity
                    && attrs.FirstOrDefault(x => x is IdentificationAttribute) is IdentificationAttribute ide)
                {
                    TryUseIdentityColumn(eb.Property(p.Name), ide.seed, ide.increment);
                }
                if (attrs.FirstOrDefault(x => x is PrecisionAttribute) is PrecisionAttribute prec)
                {
                    eb.Property(p.Name).HasPrecision(prec.precision, prec.scale);
                }

                if (attrs.FirstOrDefault(x => x is DisplayAttribute) is DisplayAttribute proDisplay)
                {
                    eb.Property(p.Name).HasComment(proDisplay.Description);
                }
            }
        }
    }
}
